﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.DataStruct;
using UtilZ.Dotnet.Ex.Log;
using UtilZ.Dotnet.Ex.Model;

namespace UtilZ.Dotnet.Ex.SequenceDB
{
#pragma warning disable 8500 // address / sizeof of managed types

    /// <summary>
    /// 顺序数据存储
    /// </summary>
    unsafe public class SequenceDatabase : TimingTaskAbs, IDisposable
    {
        private const uint _SYNC = 0xFEEF9FF9;

        private bool _disposed = false;
        private readonly object _lock = new object();


        private readonly int _expireDays;
        private readonly string _dir;
        private readonly string _extension;
        private readonly int _perFileCount;
        private readonly long _dataBeginPos;

        private FileStream _stream = null;
        private long _fileNameIndex = 0L;

        private readonly UnmanagedMemoryPointer<byte> _headPointer = new UnmanagedMemoryPointer<byte>(sizeof(SequenceDBFileHead));
        private readonly SequenceDBFileHead* _headPtr;

        private readonly UnmanagedMemoryPointer<byte> _itemHeadPointer;
        private readonly DataItemHead* _itemHeadPtr;

        private int _count = 0;
        private long _beginIndex = 0L;
        private long _endIndex = 0L;



        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="expireDays">数据文件过期时间，天数</param>
        /// <param name="dir">数据文件存储目录</param>
        /// <param name="extension">数据文件扩展名</param>
        /// <param name="perFileCount">每个文件中存储的数据记录数</param>
        public SequenceDatabase(int expireDays, string dir, string extension = ".dat", int perFileCount = 1000000)
            : base(0, 0, 0, 0, 0)
        {
            if (string.IsNullOrWhiteSpace(dir))
            {
                throw new ArgumentNullException(nameof(dir));
            }

            if (perFileCount < 1)
            {
                throw new ArgumentOutOfRangeException(nameof(perFileCount), "每个文件中存储的数据记录数不能少于1");
            }

            this._expireDays = expireDays;
            this._dir = dir;
            this._extension = extension;
            this._perFileCount = perFileCount;
            this._dataBeginPos = sizeof(SequenceDBFileHead) + sizeof(DataItemHead) * perFileCount;

            this._itemHeadPointer = new UnmanagedMemoryPointer<byte>(perFileCount * sizeof(DataItemHead));

            this._headPtr = (SequenceDBFileHead*)this._headPointer.DataPtr;
            this._headPtr->Sync = _SYNC;
            this._headPtr->Ver = 1;
            this._headPtr->Max = perFileCount;
            this._headPtr->Count = 0;
            this._headPtr->Reverse = 0;

            this._itemHeadPtr = (DataItemHead*)this._itemHeadPointer.DataPtr;
            this.LoadMerge();

            this.PrimitveExpireDelete();
            TimingTaskManager.Default.AddTask(this);
        }

        /// <summary>
        /// 执行任务
        /// </summary>
        protected override void PrimitiveExcute()
        {
            this.PrimitveExpireDelete();
        }

        /// <summary>
        /// 过期数据删除在启动或半夜的时候执行
        /// </summary>
        private void PrimitveExpireDelete()
        {
            try
            {
                if (!Directory.Exists(this._dir))
                {
                    return;
                }

                string[] fileNams = Directory.GetFiles(this._dir, $"*{this._extension}");
                long fileBeginTimestamp;

                for (int i = 0; i < fileNams.Length; i++)
                {
                    if (!long.TryParse(Path.GetFileNameWithoutExtension(fileNams[i]), out fileBeginTimestamp))
                    {
                        continue;
                    }

                    if ((DateTime.Now - TimeEx.TimestampToDateTime(fileBeginTimestamp)).TotalDays > this._expireDays)
                    {
                        FileEx.TryDeleFile(fileNams[i]);
                    }
                }
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }


        private void LoadMerge()
        {
            if (!Directory.Exists(this._dir))
            {
                return;
            }

            string[] fileNams = Directory.GetFiles(this._dir, $"*{this._extension}");
            if (fileNams.Length <= 0)
            {
                return;
            }

            NameValue<long> lastItem = null;
            long fileBeginTimestamp;

            for (int i = 0; i < fileNams.Length; i++)
            {
                if (long.TryParse(Path.GetFileNameWithoutExtension(fileNams[i]), out fileBeginTimestamp))
                {
                    if (lastItem == null)
                    {
                        lastItem = new NameValue<long>(fileNams[i], fileBeginTimestamp);
                    }
                    else
                    {
                        if (lastItem.Value < fileBeginTimestamp)
                        {
                            lastItem.Name = fileNams[i];
                            lastItem.Value = fileBeginTimestamp;
                        }
                    }
                }
                else
                {
                    Loger.Warn($"无效的数据文件\"{fileNams[i]}\".");
                }
            }

            this._stream = new FileStream(lastItem.Name, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
            this._fileNameIndex = lastItem.Value;

            Span<byte> head = this._headPointer.GetSpan();
            this._stream.Read(head);
            this._count = this._headPtr->Count;
            this._beginIndex = this._headPtr->BeginIndex;
            this._endIndex = this._headPtr->EndIndex;

            Span<byte> itemHead = this._itemHeadPointer.GetSpan(this._headPtr->Max * sizeof(DataItemHead));
            this._stream.Read(itemHead);

            this._stream.Seek(0, SeekOrigin.End);
        }

        private readonly DataSpan<byte> _dataSpan = new DataSpan<byte>();
        /// <summary>
        /// 添加一个数据
        /// </summary>
        /// <param name="data"></param>
        public void Add(ISequenceData data)
        {
            lock (this._lock)
            {
                if (this._disposed)
                {
                    return;
                }

                DataItemHead* itemHeadPtr = this._itemHeadPtr + this._count;
                itemHeadPtr->Index = data.DataIndex;

                if (this._stream == null)
                {
                    this._count = 0;
                    this._fileNameIndex = itemHeadPtr->Index;
                    DirectoryInfoEx.CheckDirectory(this._dir);
                    string filePath = PathEx.Combine(PathType.Local, this._dir, $"{this._fileNameIndex}{this._extension}");
                    this._stream = new FileStream(filePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite);
                    this._stream.Position = this._dataBeginPos;
                    this._beginIndex = itemHeadPtr->Index;
                }

                data.SetData(this._dataSpan);
                itemHeadPtr->DataPosition = this._stream.Position;
                itemHeadPtr->DataLength = this._dataSpan.Length;

                if (this._dataSpan.DataType == DataSpanDataType.Array)
                {
                    this._stream.Write(this._dataSpan.Array, this._dataSpan.Offset, this._dataSpan.Length);
                }
                else
                {
                    this._stream.Write(this._dataSpan.GetSpan());
                }

                this._count++;
                this._endIndex = itemHeadPtr->Index;

                if (this._count >= this._perFileCount)
                {
                    this.CloseFile();
                }
            }
        }

        private void CloseFile()
        {
            if (this._stream == null)
            {
                return;
            }

            this._stream.Position = 0L;

            this._headPtr->Count = this._count;
            this._headPtr->BeginIndex = this._beginIndex;
            this._headPtr->EndIndex = this._endIndex;
            Span<byte> head = this._headPointer.GetSpan();
            this._stream.Write(head);

            Span<byte> itemHead = this._itemHeadPointer.GetSpan(this._count * sizeof(DataItemHead));
            this._stream.Write(itemHead);

            this._stream.Close();
            this._stream = null;
        }

        unsafe internal List<ReplayItem> GetReplayData(long beginIndex, long endIndex)
        {
            if (!Directory.Exists(this._dir))
            {
                beginIndex = 0;
                endIndex = 0;
                return null;
            }

            string[] fileNams = Directory.GetFiles(this._dir, $"*{this._extension}");
            long fileBeginTimestamp;
            List<ReplayItem> list = null;
            ReplayItem replayData = null;

            byte[] headData = new byte[sizeof(SequenceDBFileHead)];
            fixed (byte* headDataBytePtr = headData)
            {
                SequenceDBFileHead* headDataPtr = (SequenceDBFileHead*)headDataBytePtr;

                UnmanagedMemoryPointer<byte> itemHeadPointer = new UnmanagedMemoryPointer<byte>(this._perFileCount * sizeof(DataItemHead));
                DataItemHead* itemHeadPtr = (DataItemHead*)itemHeadPointer.DataPtr;
                NumberValueRangeItem<int> indexRange;
                bool currentFlag = false;
                int count = 0;

                for (int i = 0; i < fileNams.Length; i++)
                {
                    if (!long.TryParse(Path.GetFileNameWithoutExtension(fileNams[i]), out fileBeginTimestamp))
                    {
                        Loger.Warn($"无效的数据文件\"{fileNams[i]}\".");
                        continue;
                    }

                    if (fileBeginTimestamp > endIndex)
                    {
                        continue;
                    }

                    try
                    {
                        currentFlag = false;

                        if (fileBeginTimestamp == this._fileNameIndex)
                        {
                            lock (this._lock)
                            {
                                if (fileBeginTimestamp == this._fileNameIndex)
                                {
                                    headDataPtr->Count = this._count;
                                    headDataPtr->BeginIndex = this._beginIndex;
                                    headDataPtr->EndIndex = this._endIndex;

                                    NativeMemory.Copy(this._itemHeadPtr, itemHeadPtr, (nuint)(sizeof(DataItemHead) * headDataPtr->Count));
                                    currentFlag = true;
                                }
                            }
                        }

                        FileStream stream = new FileStream(fileNams[i], FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                        if (currentFlag)
                        {

                        }
                        else
                        {
                            if (stream.Read(headData, 0, headData.Length) != headData.Length)
                            {
                                stream.Close();
                                continue;
                            }

                            if (headDataPtr->BeginIndex > endIndex || headDataPtr->EndIndex < beginIndex)
                            {
                                stream.Close();
                                continue;
                            }

                            itemHeadPointer.CheckSetLength(headDataPtr->Count * sizeof(DataItemHead));
                            stream.Position = sizeof(SequenceDBFileHead);
                            stream.Read(itemHeadPointer.GetSpan());
                        }

                        indexRange = this.FindIndex(headDataPtr->Count, itemHeadPtr, beginIndex, endIndex);
                        count = indexRange.EndValue - indexRange.BeginValue;
                        if (count > 0)
                        {
                            replayData = new ReplayItem(stream, ((DataItemHead*)itemHeadPointer.DataPtr) + indexRange.BeginValue, count);
                            if (list == null)
                            {
                                list = new List<ReplayItem>();
                            }
                            list.Add(replayData);
                        }
                    }
                    catch (Exception ex)
                    {
                        Loger.Warn(ex);
                        continue;
                    }
                }
            }

            return list;
        }

        private NumberValueRangeItem<int> FindIndex(int count, DataItemHead* itemHeadPtr, long beginTs, long endTs)
        {
            int beginIndex = -1, endIndex = -1;
            for (int i = 0; i < count; i++)
            {
                if (beginIndex < 0)
                {
                    if (itemHeadPtr[i].Index >= beginTs)
                    {
                        beginIndex = i;
                    }
                }
                else
                {
                    if (itemHeadPtr[i].Index >= endTs)
                    {
                        endIndex = i;
                        break;
                    }
                }
            }

            if (endIndex == -1)
            {
                endIndex = count;
            }

            return new NumberValueRangeItem<int>(beginIndex, endIndex, ValueRangeType.In);
        }


        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            lock (this._lock)
            {
                if (this._disposed)
                {
                    return;
                }

                this._disposed = true;

                this.CloseFile();
                this._headPointer.Dispose();
                this._itemHeadPointer.Dispose();
            }

            TimingTaskManager.Default.Remove(this);
        }

    }

    /// <summary>
    /// 顺序数据文件头结构体
    /// </summary>
    [StructLayout(LayoutKind.Explicit, Size = 36), UnsafeValueType, CompilerGenerated]
    internal struct SequenceDBFileHead
    {
        /// <summary>
        /// 同步头,固定0xFEEF9FF9
        /// </summary>
        [FieldOffset(0)]
        public uint Sync;

        /// <summary>
        /// 版本号
        /// </summary>
        [FieldOffset(4)]
        public int Ver;

        /// <summary>
        /// 最大数据记录数
        /// </summary>
        [FieldOffset(8)]
        public int Max;

        /// <summary>
        /// 实际数据记录数
        /// </summary>
        [FieldOffset(12)]
        public int Count;

        /// <summary>
        /// 数据记录起始索引
        /// </summary>
        [FieldOffset(16)]
        public long BeginIndex;

        /// <summary>
        /// 数据记录截止索引
        /// </summary>
        [FieldOffset(24)]
        public long EndIndex;

        /// <summary>
        /// 保留数据
        /// </summary>
        [FieldOffset(32)]
        public int Reverse;
    }

    /// <summary>
    /// 顺序数据项结构体
    /// </summary>
    [StructLayout(LayoutKind.Explicit, Size = 20), UnsafeValueType, CompilerGenerated]
    internal struct DataItemHead
    {
        [FieldOffset(0)]
        public long Index;

        [FieldOffset(8)]
        public long DataPosition;

        [FieldOffset(16)]
        public int DataLength;

    }

}
